﻿//
//  Monkey Manager
//  Copyright (C) 2010 Victor Tereschenko (aka sibvic)
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//     
// ========================================================================

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using ManagerDB.Utils;

namespace ManagerDB
{
    public class SQLiteDBController : IMutableDatabase
    {
        public SQLiteDBController(string dbFileName, AutoTag[] autoTags, string replacementsFileName)
        {
            mFileNameGenerator = new FileNameGenerator(replacementsFileName);
            var storagesConfigPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "storages.xml");
            mStorages = ManagerDB.SQLiteDB.StoragesReader.Read(storagesConfigPath);
            if (mStorages == null)
                mStorages = ManagerDB.SQLiteDB.StoragesReader.CreateDefault();
            mTagger = new AutoTagger(autoTags);
            mDatabase = new Database(dbFileName);
        }

        Storage[] mStorages;
        AutoTagger mTagger;
        Database mDatabase;
        FileNameGenerator mFileNameGenerator;

        public StoredFile Find(string tth, long size)
        {
            return mDatabase.Find(tth, size);
        }

        public StoredFile[] Find(Filter filter)
        {
            return mDatabase.Find(filter);
        }

        public void Update(StoredFile file)
        {
            if (file.File.FileName != "")
                moveFile(file);
            mDatabase.Update(file);
        }

        private void moveFile(StoredFile file)
        {
            string newFileName = mFileNameGenerator.GenerateName(file.Title, file.File);
            if (newFileName != file.File.FileName)
            {
                Storage storage = mStorages.First(st => st.ID == (int)file.File.LocationID);
                var sourcePath = System.IO.Path.Combine(storage.Path, file.File.FileName);
                if (!File.Exists(sourcePath))
                    return;
                newFileName = SQLiteDBController.GetUniqueStorageFileName(storage, newFileName);
                var targetPath = System.IO.Path.Combine(storage.Path, newFileName);
                try
                {
                    System.IO.File.Move(sourcePath, targetPath);
                }
                    catch (UnauthorizedAccessException)
                    {
                    }
                finally
                {
                    file.File.FileName = newFileName;
                }
            }
        }

        public string GetPath(StoredFile file)
        {
            Storage storage = mStorages.First(st => st.ID == file.File.LocationID);
            if (storage == null)
                return null;

            return Path.Combine(storage.Path, file.File.FileName);
        }

        /// <summary>
        /// Updaload a file into the DB.
        /// </summary>
        public StoredFile Add(FileHashInfo fileToAdd, bool move)
        {
            if (!File.Exists(fileToAdd.FileInfo.FullName))
                throw new Exception("File not found");
            Storage storage = getBestStorage();
            if (storage == null)
                throw new Exception("No storages defined");
            if (storage.FreeSpace < fileToAdd.FileInfo.Length)
                throw new Exception("Not enought free space");

            string name = Path.GetFileNameWithoutExtension(fileToAdd.FileInfo.FullName);
            string extension = Path.GetExtension(fileToAdd.FileInfo.FullName);
            string fileName = mFileNameGenerator.GenerateName(name, extension);
            if (fileName == null)
                fileName = name + extension;

            fileName = GetUniqueStorageFileName(storage, fileName);
            if (!Directory.Exists(storage.Path))
                Directory.CreateDirectory(storage.Path);
            if (move)
                File.Move(fileToAdd.FileInfo.FullName, Path.Combine(storage.Path, fileName));
            else
                File.Copy(fileToAdd.FileInfo.FullName, Path.Combine(storage.Path, fileName));

            var file = mDatabase.AddFile(storage.ID, fileName);
            file.Title = Path.GetFileNameWithoutExtension(fileToAdd.FileInfo.FullName);
            file.File.ID.TTH = fileToAdd.TTH;
            AutoTagger.Result res = mTagger.GetTags(file.Title);
            foreach (string tags in res.Tags)
                foreach(string tag in StoredFileController.GetTags(tags))
                    if (!file.Tags.Contains(tag))
                        file.Tags.Add(tag);
            file.Year = res.Year;
            file.Month = res.Month;
            file.Day = res.Day;
            Update(file);
            return file;
        }

        /// <summary>
        /// Removes file.
        /// </summary>
        /// <param name="file">File to remove.</param>
        public void Remove(StoredFile file)
        {
            string fileName = GetPath(file);
            if (fileName == null)
                return;
            if (File.Exists(fileName))
            {
                System.IO.FileAttributes attrs = System.IO.File.GetAttributes(fileName);
                if (attrs.HasFlag(System.IO.FileAttributes.ReadOnly))
                    System.IO.File.SetAttributes(fileName, System.IO.FileAttributes.Normal);
                File.Delete(fileName);
            }
            // TODO check for exception
            mDatabase.DeleteFile(file.ID);
        }

        public static string GetUniqueStorageFileName(Storage storage, string fileName)
        {
            string generatedPath = Path.Combine(storage.Path, fileName);
            if (!File.Exists(generatedPath))
                return fileName;

            string fileNameWOExt = Path.GetFileNameWithoutExtension(fileName);
            string fileExtension = Path.GetExtension(fileName);
            for (uint i = 1; i < uint.MaxValue; ++i)
            {
                fileName = fileNameWOExt + "_" + i.ToString() + fileExtension;
                generatedPath = Path.Combine(storage.Path, fileNameWOExt + i.ToString() + fileExtension);
                if (!File.Exists(generatedPath))
                    return fileName;
            }

            throw new Exception("Too many files with the same name");
        }

        /// <summary>
        /// Gets the best storage.
        /// </summary>
        /// <returns>The best storage.</returns>
        private Storage getBestStorage()
        {
            if (mStorages.Length == 0)
                return null;
            Storage storage = mStorages[0];
            for (int i = 1; i < mStorages.Length; ++i)
                if (storage.FreeSpace < mStorages[i].FreeSpace)
                    storage = mStorages[i];

            return storage;
        }

        bool mDisposed = false;
        public void Dispose()
        {
            if (mDisposed)
                return;
            mDisposed = true;
        }
    }
}
